package org.shunya.duplicatefileperquisitor;

import javafx.application.Application;
import javafx.beans.InvalidationListener;
import javafx.beans.Observable;
import javafx.beans.binding.ListBinding;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ButtonBuilder;
import javafx.scene.control.CheckBox;
import javafx.scene.control.CheckBoxBuilder;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.layout.VBoxBuilder;
import javafx.stage.Stage;
import javafx.util.Callback;

/**
 *
 * A simple table that uses cell factories to add a control to a table column.
 *
 * @see javafx.scene.control.TableCell
 * @see javafx.scene.control.TableColumn
 * @see javafx.scene.control.TablePosition
 * @see javafx.scene.control.TableRow
 * @see javafx.scene.control.TableView
 */

public class TableViewCheckBoxColumnDemo extends Application {

    private CheckBox selectAllCheckBox;
    private TableView<Employee> employeeTable;
    private Button exportButton;

    private final EventHandler<ActionEvent> event = new EventHandler<ActionEvent>() {
        @Override
        public void handle(ActionEvent event) {

            // Checking for an unselected employee in the table view.
            boolean unSelectedFlag = false;
            for (Employee item : getEmployeeTable().getItems()) {
                if (!item.getSelected()) {
                    unSelectedFlag = true;
                    break;
                }
            }
			/*
			 * If at least one employee is not selected, then deselecting the check box in the table column header, else if all employees
			 * are selected, then selecting the check box in the header.
			 */
            if (unSelectedFlag) {
                getSelectAllCheckBox().setSelected(false);
            } else {
                getSelectAllCheckBox().setSelected(true);
            }

            // Checking for a selected employee in the table view.
            boolean selectedFlag = false;
            for (Employee item : getEmployeeTable().getItems()) {
                if (item.getSelected()) {
                    selectedFlag = true;
                    break;
                }
            }
			/*
			 * If at least one employee is selected, then enabling the "Export" button, else if none of the employees are selected, then
			 * disabling the "Export" button.
			 */
            if (selectedFlag) {
                enableExportButton();
            } else {
                disableExportButton();
            }
        }
    };

    private void init(Stage primaryStage) {
        StackPane root = new StackPane();
        Scene s = new Scene(root);
        primaryStage.setScene(s);

        VBox vb = VBoxBuilder.create().padding(new Insets(15)).spacing(15).build();
        vb.getChildren().addAll(getEmployeeTable(), getExportButton());

        root.getChildren().addAll(vb);
    }

    /**
     * Lazy getter for the selectAllCheckBox.
     *
     * @return selectAllCheckBox
     */
    public CheckBox getSelectAllCheckBox() {
        if (selectAllCheckBox == null) {
            final CheckBox selectAllCheckBox = CheckBoxBuilder.create().build();

            // Adding EventHandler to the CheckBox to select/deselect all employees in table.
            selectAllCheckBox.setOnAction(new EventHandler<ActionEvent>() {
                @Override
                public void handle(ActionEvent event) {
                    // Setting the value in all the employees.
                    for (Employee item : getEmployeeTable().getItems()) {
                        item.setSelected(selectAllCheckBox.isSelected());
                    }
                    getExportButton().setDisable(!selectAllCheckBox.isSelected());
                }
            });

            this.selectAllCheckBox = selectAllCheckBox;
        }
        return selectAllCheckBox;
    }

    @SuppressWarnings("unchecked")
    public TableView<Employee> getEmployeeTable() {
        if (employeeTable == null) {
            // "Selected" column
            TableColumn<Employee, Boolean> selectedCol = new TableColumn<Employee, Boolean>();
            selectedCol.setMinWidth(50);
            selectedCol.setGraphic(getSelectAllCheckBox());
            selectedCol.setCellValueFactory(new PropertyValueFactory<Employee, Boolean>("selected"));
            selectedCol.setCellFactory(new Callback<TableColumn<Employee, Boolean>, TableCell<Employee, Boolean>>() {
                public TableCell<Employee, Boolean> call(TableColumn<Employee, Boolean> p) {
                    final TableCell<Employee, Boolean> cell = new TableCell<Employee, Boolean>() {
                        @Override
                        public void updateItem(final Boolean item, boolean empty) {
                            if (item == null)
                                return;
                            super.updateItem(item, empty);
                            if (!isEmpty()) {
                                final Employee employee = getTableView().getItems().get(getIndex());
                                CheckBox checkBox = new CheckBox();
                                checkBox.selectedProperty().bindBidirectional(employee.selectedProperty());
                                // checkBox.setOnAction(event);
                                setGraphic(checkBox);
                            }
                        }
                    };
                    cell.setAlignment(Pos.CENTER);
                    return cell;
                }
            });
            // "First Name" column
            TableColumn<Employee, String> firstNameCol = new TableColumn<Employee, String>();
            firstNameCol.setMinWidth(200);
            firstNameCol.setText("First Name");
            firstNameCol.setCellValueFactory(new PropertyValueFactory<Employee, String>("firstName"));

            // "Last Name" column
            TableColumn<Employee, String> lastNameCol = new TableColumn<Employee, String>();
            lastNameCol.setMinWidth(200);
            lastNameCol.setText("Last Name");
            lastNameCol.setCellValueFactory(new PropertyValueFactory<Employee, String>("lastName"));

            // "Email" column
            TableColumn<Employee, String> emailCol = new TableColumn<Employee, String>();
            emailCol.setText("Email");
            emailCol.setMinWidth(200);
            emailCol.setCellValueFactory(new PropertyValueFactory<Employee, String>("email"));

            final TableView<Employee> tableView = new TableView<Employee>();
            tableView.setItems(getEmployeeData());
            tableView.getColumns().addAll(selectedCol, firstNameCol, lastNameCol, emailCol);

            ListBinding<Boolean> lb = new ListBinding<Boolean>() {
                {
                    bind(tableView.getItems());
                }

                @Override
                protected ObservableList<Boolean> computeValue() {
                    ObservableList<Boolean> list = FXCollections.observableArrayList();
                    for (Employee p : tableView.getItems()) {
                        list.add(p.getSelected());
                    }
                    return list;
                }
            };

            lb.addListener(new ChangeListener<ObservableList<Boolean>>() {
                @Override
                public void changed(ObservableValue<? extends ObservableList<Boolean>> arg0, ObservableList<Boolean> arg1,
                                    ObservableList<Boolean> l) {
                    // Checking for an unselected employee in the table view.
                    boolean unSelectedFlag = false;
                    for (boolean b : l) {
                        if (!b) {
                            unSelectedFlag = true;
                            break;
                        }
                    }
					/*
					 * If at least one employee is not selected, then deselecting the check box in the table column header, else if all
					 * employees are selected, then selecting the check box in the header.
					 */
                    if (unSelectedFlag) {
                        getSelectAllCheckBox().setSelected(false);
                    } else {
                        getSelectAllCheckBox().setSelected(true);
                    }

                    // Checking for a selected employee in the table view.
                    boolean selectedFlag = false;
                    for (boolean b : l) {
                        if (!b) {
                            selectedFlag = true;
                            break;
                        }
                    }

					/*
					 * If at least one employee is selected, then enabling the "Export" button, else if none of the employees are selected,
					 * then disabling the "Export" button.
					 */
                    if (selectedFlag) {
                        enableExportButton();
                    } else {
                        disableExportButton();
                    }
                }
            });

            tableView.getItems().addListener(new InvalidationListener() {

                @Override
                public void invalidated(Observable arg0) {
                    System.out.println("invalidated");
                }
            });
            tableView.getItems().addListener(new ListChangeListener<Employee>() {

                @Override
                public void onChanged(javafx.collections.ListChangeListener.Change<? extends Employee> arg0) {
                    System.out.println("changed");
                }
            });
            this.employeeTable = tableView;

        }
        return employeeTable;
    }

    public Button getExportButton() {
        if (this.exportButton == null) {
            final Button exportButton = ButtonBuilder.create().text("Export").build();
            exportButton.setOnAction(new EventHandler<ActionEvent>() {
                @Override
                public void handle(ActionEvent arg0) {
                    System.out.print("employees Selected : [");
                    for (Employee employee : getEmployeeTable().getItems()) {
                        if (employee.getSelected()) {
                            System.out.print(employee.getFirstName() + ", ");
                        }
                    }
                    System.out.print(" ]\n");
                }
            });
            exportButton.setDisable(true);
            this.exportButton = exportButton;
        }
        return this.exportButton;
    }

    /**
     * Enables the "Export" button.
     */
    public void enableExportButton() {
        getExportButton().setDisable(false);
    }

    /**
     * Disables the "Export" button.
     */
    public void disableExportButton() {
        getExportButton().setDisable(true);
    }

    // Employee object
    public static class Employee {

        private SimpleBooleanProperty selected;
        private StringProperty firstName;
        private StringProperty lastName;
        private StringProperty email;

        private Employee(boolean selected, String fName, String lName, String email) {
            this.selected = new SimpleBooleanProperty(selected);
            this.firstName = new SimpleStringProperty(fName);
            this.lastName = new SimpleStringProperty(lName);
            this.email = new SimpleStringProperty(email);
        }

        public BooleanProperty selectedProperty() {
            return selected;
        }

        public boolean getSelected() {
            return selected.get();
        }

        public void setSelected(boolean selected) {
            this.selected.set(selected);
        }

        public StringProperty firstNameProperty() {
            return firstName;
        }

        public String getFirstName() {
            return firstName.get();
        }

        public String getLastName() {
            return lastName.get();
        }

        public String getEmail() {
            return email.get();
        }

        public StringProperty lastNameProperty() {
            return lastName;
        }

        public StringProperty emailProperty() {
            return email;
        }

        public void setLastName(String lastName) {
            this.lastName.set(lastName);
        }

        public void setFirstName(String firstName) {
            this.firstName.set(firstName);
        }

        public void setEmail(String email) {
            this.email.set(email);
        }
    }

    private ObservableList<Employee> getEmployeeData() {
        ObservableList<Employee> list = FXCollections.observableArrayList();
        for (int i = 0; i < 1; i++) {
            list.add(new Employee(false, "Jacob", "Smith", "jacob.smith@example.com"));
            list.add(new Employee(false, "Jacob", "Smith", "ethan.williams@example.com"));
            list.add(new Employee(false, "Isabella", "Johnson", "isabella.johnson@example.com"));
            list.add(new Employee(false, "Ethan", "Williams", "ethan.williams@example.com"));
            list.add(new Employee(false, "Emma", "Jones", "emma.jones@example.com"));
        }
        return list;
    }

    @Override
    public void start(Stage primaryStage) throws Exception {
        init(primaryStage);
        primaryStage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }

}
